using System;
using System.ComponentModel.Composition;
using AzureContrib.WindowsAzure.Utils;
using SD = System.Diagnostics;

namespace AzureContrib.WindowsAzure.Diagnostics
{
	/// <summary>
	/// <see cref="ITraceFactory"/>
	/// </summary>
	[Export(typeof(ITraceFactory))]
	public class WindowsAzureTraceFactory : ITraceFactory
	{
		private ITraceSwitchProvider traceSwitchProvider;
		private ITrace internalTrace;
		internal Func<Type, ITraceSwitchProvider, ITrace> CreateTraceForType = DoCreate;
		internal Func<string, ITraceSwitchProvider, ITrace> CreateTraceForSwitch = DoCreate;

		/// <summary>
		/// Default trace level is <see cref="SD.TraceLevel.Warning"/>
		/// </summary>
		[ImportingConstructor]
		public WindowsAzureTraceFactory()
			: this(null)
		{ }

		/// <summary>
		/// Default trace level is <see cref="SD.TraceLevel.Warning"/>
		/// </summary>
		public WindowsAzureTraceFactory(SD.TraceLevel defaultTraceLevel)
			: this(null, defaultTraceLevel)
		{ }

		/// <summary>
		/// Ctor
		/// </summary>
		/// <param name="defaultTraceLevel">Default trace level is <see cref="SD.TraceLevel.Warning"/></param>
		/// <param name="traceSwitchProvider">The control that performs checks on what to log.</param>
		internal WindowsAzureTraceFactory(ITraceSwitchProvider traceSwitchProvider, SD.TraceLevel defaultTraceLevel = SD.TraceLevel.Warning)
		{
			this.traceSwitchProvider = traceSwitchProvider ?? new WindowsAzureTraceSwitchProvider(defaultTraceLevel);

			internalTrace = DoCreate(GetType(), this.traceSwitchProvider);

			internalTrace.Verbose("Created internal trace for the type {0}.".F(GetType().FullName));
		}

		/// <summary>
		/// Create a fully loaded instance of an <see cref="ITrace"/>.
		/// </summary>
		/// <param name="toLogFor">The type to create the trace for.</param>
		/// <returns><see cref="ITrace"/></returns>
		public ITrace Create(Type toLogFor)
		{
			if (toLogFor == null) throw new ArgumentNullException("toLogFor");

			var trace = CreateTraceForType(toLogFor, traceSwitchProvider);

			internalTrace.Verbose("Created ITrace for the type {0}.".F(toLogFor.FullName));

			return trace;
		}

		/// <summary>
		/// Create a fully loaded instance of an <see cref="ITrace"/>.
		/// </summary>
		/// <param name="switch">The name of the switch this trace is for.</param>
		/// <returns><see cref="ITrace"/></returns>
		public ITrace Create(string @switch)
		{
			if (@switch == null) throw new ArgumentNullException("switch");
			if (string.IsNullOrWhiteSpace(@switch)) throw new ArgumentOutOfRangeException("switch");

			var trace = CreateTraceForSwitch(@switch, traceSwitchProvider);

			internalTrace.Verbose("Created ITrace for the type {0}.".F(@switch));

			return trace;
		}

		private static ITrace DoCreate(Type toLogFor, ITraceSwitchProvider traceSwitchProvider)
		{
			return new Trace(toLogFor, traceSwitchProvider);
		}

		private static ITrace DoCreate(string @switch, ITraceSwitchProvider traceSwitchProvider)
		{
			return new Trace(@switch, traceSwitchProvider);
		}
	}
}